package menus.common.statemachines;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.lang.reflect.Field;
import java.util.Date;

import menus.common.AbstractItem;
import menus.common.AbstractMenu;

import fr.lri.swingstates.canvas.CPolyLine;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.Gesture;
import fr.lri.swingstates.sm.State;
import fr.lri.swingstates.sm.StateNotFoundException;
import fr.lri.swingstates.sm.Transition;
import fr.lri.swingstates.sm.transitions.Press;

/**
 * Skeleton for a marking menu state machine.
 * 
 * @author mathieu
 *
 */
public abstract class AbstractMenuStateMachine extends CStateMachine {
	
	/**
	 * Label for the "inactive" state
	 */
	protected static final String INACTIVE_STATE = "inactif";
	
	/**
	 * Label for the "selection" state
	 */
	protected static final String SELECTION_STATE = "selection";
	
	/**
	 * Label for the "triggered" state
	 */
	protected static final String TRIGGERED_STATE = "triggered";
	
	
	
	/**
	 * Menu to which is attached this state machine
	 */
	protected AbstractMenu menu;
	
	/**
	 * Gesture made by the user, used to determine the selected item (if any)
	 */
	protected Gesture gesture;
	
	/**
	 * Mark drawn by the user. Only for visual feedback.
	 */
	protected CPolyLine mark;
	
	/**
	 * Location of the menu for its last call.
	 */
	protected Point2D where;
	
	/**
	 * Selected item
	 */
	protected AbstractItem selected;
	
	
//	/**
//	 * Boolean indicating that this state machine already has been upgraded.
//	 * Upgrades are called by states machines extending this skeleton.
//	 */
//	protected boolean upgraded = false;
	
	/**
	 * Temporal threshold. 
	 * TODO Not used right now
	 */
	protected int startTreshold = 1000;
	
	/**
	 * Timestamp of the selection beginning
	 */
	protected long selectionStart = -1;
	
	/**
	 * Duration of the selection
	 */
	protected long selectionDuration = -1;
	
	/**
	 * False if a selection is occurring, true either
	 * Used for the experiment
	 */
	protected boolean selectionEnded = false;
	
	
	
	// ----------------------------------------------------------------- XXX

	
	
	/*
	 * The transitions are created outside of their states so they can easily be removed and 
	 * replaced in extending states machine. This also allows to use their action() method.
	 * 
	 *  Be careful though, action() methods using for example this.getPoint() are to be rewritten, 
	 *  because getPoint() doesn't apply to a transition calling it from outside.  
	 */
	
	/**
	 * Transition fired when the user starts his selection. Triggered by a simple Press event.
	 */
	protected Transition startSelection = new Press(">> " + TRIGGERED_STATE){
		
		@Override
		public void action() {
			
			System.out.println("AbstractMenuStateMachine.startSelection.action()");
			
			// Initializing the mark to the event location and showing it
			mark.reset(getPoint());
			mark.setDrawable(true);
			
			// Also initializing the gesture
			gesture.addPoint(getPoint().getX(), getPoint().getY());
			
			// ...and finally initializing the location of the menu
			where = getPoint();
			
			selectionStart = System.currentTimeMillis();
			
		}
		
	};
	
	
	
	/**
	 * Initial state. Waits for the selection to begin.
	 */
	public State inactive = new State(INACTIVE_STATE){
		
		@Override
		public void enter() {
			
			// Resetting the gesture and hiding the mark
			gesture.reset();
			mark.setDrawable(false);
			
			// Already done but who knows...
			selectionEnded = false;
			
		}
		
	};
	
	/**
	 * Intermediate state. Used to differentiate the different selection ways
	 * (one-stroke, tap-and-stroke, etc.)
	 */
	public State triggered = new State(TRIGGERED_STATE){
		
		@Override
		public void enter() {}
		
	};
	
	public State selection = new State(SELECTION_STATE){
		
		@Override
		public void enter() {}
		
		@Override
		public void leave() {
			
			selectionEnded = true;
			
			selectionDuration = System.currentTimeMillis() - selectionStart;
			
			menu.setExperimentMeasures();
			
		}
		
	};
	
	
	
	// ----------------------------------------------------------------- XXX

	
	
	/**
	 * Default constructor
	 * @param canvas
	 * @param menu
	 */
	public AbstractMenuStateMachine(Canvas canvas, AbstractMenu menu){
		
		super(canvas);
		
		this.menu = menu;
		
		this.gesture = new Gesture();
		
		this.mark = menu.getCanvas().newPolyLine();
		this.mark.setAntialiased(true)
				.setFilled(false)
				.setOutlinePaint(Color.blue)
				.setStroke(new BasicStroke(3))
				.setDrawable(false)
				.setPickable(false);
		
		this.initialState = this.currentState = inactive;
		
		inactive.addTransition(startSelection);
		
	}
	
	/**
	 * @param canvas
	 * @param menu
	 * @param treshold
	 */
	public AbstractMenuStateMachine(Canvas canvas, AbstractMenu menu, int treshold){
		
		this(canvas, menu);
		this.startTreshold = treshold;
		
	}
	
//	/**
//	 * Upgrading method that can be extended. Called right before the state machine is truly 
//	 * initialized by the SwingStates library.
//	 */
//	protected void upgradeStateMachine(){};
	
	/**
	 * Method calling the ghost() method of the specified item.
	 * @param where
	 * @param selected
	 */
	protected void callGhost(Point2D where, AbstractItem selected){
		
		if (selected != null && where != null)
			selected.ghost(where);
		else {
			if (selected == null)
				System.out.println("selected null");
			if (where == null)
				System.out.println("where null");
		}
		
	}
	
	/**
	 * Method calling the ghost() method of the selected item.
	 * @param where
	 */
	protected void callGhost(Point2D where){
		callGhost(where, selected);
	}
	
//	/**
//	 * Extends and calls the CStateMachine initStatesAndTransitions() method, but not before
//	 * having "upgraded" this state machine with additional states and transitions.
//	 * This way we are sure these modifications are taken account.
//	 * ( CStateMachine.initStatesAndTransitions() can only be called once )
//	 */
//	@Override
//	public void initStatesAndTransitions() {
//		
//		System.out.println("AbstractMenuStateMachine.initStatesAndTransitions()");
//		System.out.println("\t" + (upgraded?"Already upgraded":"Upgrade"));
//		
//		if (!upgraded){
//			upgradeStateMachine();
//			upgraded = true;
//		}
//		
//		super.initStatesAndTransitions();
//			
//	}
	
	/**
	 * @return
	 */
	public CPolyLine getMark(){
		return mark;
	}
	
	public long[] getSelectionTimes(){
		return new long[]{selectionStart, selectionDuration};
	}
	
	public void resetSelection(){
		selectionEnded = false;
	}
	
	public boolean isSelectionEnded(){
		return selectionEnded;
	}
	
	@Override
	public String toString() {
		String result = "\n";
		
		result += "State Machine :\n";
		for (State s : this.getAllStates()){
			result += "\tState " + s.getName() + "\n";
			for (Transition t : s.getTransitions()){
				result += "\t\t" + t.toString() + "\n";
			}
		}
		result += "\n";
		
		return result;
	}
	
}
